#include "StdAfx.h"
#include "IrrInput.h"
#include <iostream>

#include "../Kernel.h"
#include <irrlicht.h>
#include <irrString.h>
#include <Keycodes.h>
#include <IEventReceiver.h>

extern Kernel* g_kernel;

extern irr::IrrlichtDevice* g_IrrDevice;     // Irr Link;

using namespace irr;    // to avoid retyping irr::

#define NEXT_TIME  (0.036f) // TODO - make a const
#define DELAY_TIME (0.5f - (NEXT_TIME))


/**
    Convert Irrlichts Mouse sys to Myriads
*/
void ToMyriadMousesym( int button, int &myriadButton, int &modifier )
{
    /**
        Button Down
    */
    if( button & irr::EMIE_LMOUSE_PRESSED_DOWN )
    {
        myriadButton = MMB_BUTTON1;
        modifier |= MouseEvent::BUTTON_DOWN;
    } 
    else if( button & irr::EMIE_RMOUSE_PRESSED_DOWN )
    {
        myriadButton = MMB_BUTTON2;  
        modifier |= MouseEvent::BUTTON_DOWN;
    }
    else if( button & irr::EMIE_MMOUSE_PRESSED_DOWN )
    {
        myriadButton = MMB_BUTTON3;  
        modifier |= MouseEvent::BUTTON_DOWN;
    }
    /**
        Button Up
    */
    if( button & irr::EMIE_LMOUSE_LEFT_UP )
    {
        myriadButton = MMB_BUTTON1;
        modifier |= MouseEvent::BUTTON_RELEASED;
    } 
    else if( button & irr::EMIE_RMOUSE_LEFT_UP )
    {
        myriadButton = MMB_BUTTON2;  
        modifier |= MouseEvent::BUTTON_RELEASED;
    }
    else if( button & irr::EMIE_MMOUSE_LEFT_UP )
    {
        myriadButton = MMB_BUTTON3;  
        modifier |= MouseEvent::BUTTON_RELEASED;
    }
    //else if ( button & irr::EMIE_MOUSE_WHEEL )
    //{
    //    modifier |= MouseEvent::WHEEL;
    //}
    else if ( button & irr::EMIE_MOUSE_MOVED )
    {
        modifier |= MouseEvent::MOTION;
    }
}
/**
    Correct Irrlichts gayness with key syms
 */
int ToMyriadKeysym( int key, bool shift )
{
    int mkey = key;
    switch( key )
    {
    case KEY_F1:
        mkey = MK_F1;
        break;

    case KEY_F2: mkey = MK_F2;
        break;

    case KEY_F3: mkey = MK_F3;
        break;

    case KEY_F4: mkey = MK_F4;
        break;

    case KEY_F5: mkey = MK_F5;
        break;

    case KEY_F6: mkey = MK_F6;
        break;

    case KEY_F7: mkey = MK_F7;
        break;

    case KEY_F8: mkey = MK_F8;
        break;

    case KEY_F9: mkey = MK_F9;
        break;

    case KEY_F10: mkey = MK_F10;
        break;

    case KEY_F11: mkey = MK_F11;
        break;

    case KEY_F12: mkey = MK_F12; 
        break;

    case KEY_SCROLL: mkey = MK_SCROLLOCK;
        break;

    case 0xC0:  /** ~ TILDE */
        mkey = MK_BACKQUOTE;
        break;

    case KEY_MINUS: mkey =  /*shift ? MK_UNDERSCORE :*/ MK_MINUS;
        break;
    case KEY_HOME: mkey = MK_HOME;
        break;
    case 187: mkey = MK_EQUALS; /** TODO */
        break;
    case KEY_INSERT: mkey = MK_INSERT;
        break;
    case KEY_PRIOR: mkey = MK_PAGEUP;
        break;
    case KEY_NEXT: mkey = MK_PAGEDOWN;
        break;
    case KEY_END: mkey = MK_END;
        break;
    case KEY_DELETE: mkey = MK_DELETE;
        break;
    case 0xDC:      /** \ BACKSLASH */
        mkey = /*shift ? MK_PIPE :*/ MK_BACKSLASH;
        break;
    case 0xDD:      /** ] Right Bracket */
        mkey = /*shift ? MK_R */MK_RIGHTBRACKET;
        break;
    case 0xDB:      /** [ Left Bracket */
        mkey = MK_LEFTBRACKET;
        break;
    case KEY_CAPITAL: mkey = MK_CAPSLOCK;
        break;
    case KEY_SHIFT: /** Should be left shift */
        mkey = MK_RSHIFT;
    //case KEY_SHIFT:
        break;
    case KEY_CONTROL: mkey = MK_LCTRL;
        break;
    case KEY_COMMA: mkey = /*shift ? MK_LESS :*/ MK_COMMA;
        break;
    case KEY_PERIOD: mkey = /*shift ? MK_GREATER :*/ MK_PERIOD;
        break;
    case 0xBF:      /** / Forwad slash */
        mkey = /*shift ? MK_QUESTION :*/ MK_SLASH;
        break;
    case 0XBA:      /** ; Semi-colon */
        mkey = /*shift ? MK_COLON :*/ MK_SEMICOLON;
        break;

    case 0xDE:      /** ' QUOTE */
        mkey =  MK_QUOTE;
        break;
    case KEY_LEFT: mkey = MK_LEFT;
        break;
    case KEY_UP: mkey = MK_UP;
        break;
    case KEY_DOWN: mkey = MK_DOWN;
        break;
    case KEY_RIGHT: mkey = MK_RIGHT;
        break;
    case KEY_NUMLOCK: mkey = MK_NUMLOCK;
        break;
    case KEY_KEY_1: mkey = MK_1;//if ( shift ) mkey = MK_EXCLAIM;
        break;
    case KEY_KEY_2: mkey = MK_2;//if ( shift ) mkey = MK_AT;
        break;
    case KEY_KEY_3: mkey = MK_3;//if ( shift ) mkey = MK_HASH;
        break;
    case KEY_KEY_4: mkey = MK_4;//if ( shift ) mkey = MK_DOLLAR;
        break;
    case KEY_KEY_5: mkey = MK_5;//if ( shift ) mkey = MK_PERCENT; // WTF?
        break;
    case KEY_KEY_6: mkey = MK_6;//if ( shift ) mkey = MK_CARET;
        break;
    case KEY_KEY_7: mkey = MK_7;//if ( shift ) mkey = MK_AMPERSAND;
        break;
    case KEY_KEY_8: mkey = MK_8;//if ( shift ) mkey = MK_ASTERISK;
        break;
    case KEY_KEY_9: mkey = MK_9;//if ( shift ) mkey = MK_LEFTPAREN;
        break;
    case KEY_KEY_0: mkey = MK_0;//if ( shift ) mkey = MK_RIGHTPAREN;
        break;
    //case KEY_     /** TODO KEYPAD */
    //    break;
    default:
        if ( key >= 65 && key <= 122 )
        {
            mkey += 32;
            //if ( shift ) mkey -= 32;

            return mkey;
        }
        //} else if ( (key > 31 && key < 127) ) 
        //    return key;//
        //else mkey = 37;//MK_UNKNOWN;
        break;
    };
    return mkey;
}


IrrInput::IrrInput(void)
{
    m_register = true;

    for ( int i = 0; i < MAX_KEYS; i++ ) {
        key[i].isDown   = false;
        key[i].Released = false;
        key[i].TimeStamp    = 0;
        key[i].NextTime     = 0;
        key[i].TimeStamp    = 0;
    }
    for ( int i = 0; i < MAX_BUTTONS; i++ )
        button[i] = false;

    // clear the joystick buttons
    for ( int i = 0; i < MAX_JOYSTICKBUTTONS; i++ ) {
        joyStickButtons[i].isDown   = false;
        joyStickButtons[i].Released = false;
        joyStickButtons[i].TimeStamp    = 0;
        joyStickButtons[i].NextTime     = 0;
    }
}

IrrInput::~IrrInput(void)
{
}


/** Initialize the input system */
int IrrInput::Init( const InputSettings &settings  )
{
    int noSuccess = 1;
    if ( g_IrrDevice )
    {
        g_IrrDevice->setEventReceiver( this );
        g_kernel->LPrintf("--Input System Online!");
        noSuccess = 0;
    } 
    else
    {
        g_kernel->LPrintf("ERROR:: Could not initialize the Input System: No IrrDevice Attached!");
    }
    
    return noSuccess;
}

/** Poll for events */
void IrrInput::Update(float dt)
{
    switch( m_IrrEvent.EventType )
    {
        /*-------------------------------------------------------------------
            Keyboard Events
        --------------------------------------------------------------------*/
    case irr::EET_KEY_INPUT_EVENT:
        HandleKeyEvent( dt );
        break;

        /*-------------------------------------------------------------------
            Mouse Events
        --------------------------------------------------------------------*/
    case irr::EET_MOUSE_INPUT_EVENT:
        HandleMouseEvent();
        break;
    case irr::EET_GUI_EVENT: 
        // savely exit the game
        g_kernel->StopLoop();
        break;
    default:
        // ignore
        break;
    }
}

/** Shutdown */
void IrrInput::Shutdown()
{
    if ( g_IrrDevice )
    {
        g_IrrDevice->setEventReceiver( NULL );
    }
}

/** Irrlicht interface */
bool IrrInput::OnEvent(irr::SEvent evt )
{
    m_IrrEvent = evt;
    return false;
}

/** Handle a Mouse Event */
void IrrInput::HandleMouseEvent()
{
    int modifier    = 0;
    int mouseButton = 0;

    // Get the key and type of event
    ToMyriadMousesym( m_IrrEvent.MouseInput.Event, mouseButton, modifier );

    if ( m_IrrEvent.MouseInput.Wheel > 0 )
        modifier |= MMB_WHEELUP;

    if ( m_IrrEvent.MouseInput.Wheel < 0 )
        modifier |= MMB_WHEELDOWN;

    MouseEvent  mouseEvent( mouseButton, m_IrrEvent.MouseInput.X, m_IrrEvent.MouseInput.Y, modifier );

    ProcessMouseEvent( &mouseEvent );
}

/** Handle a Key Event */
void IrrInput::HandleKeyEvent( float dt )
{
    int keyStroke   = ToMyriadKeysym( m_IrrEvent.KeyInput.Key, m_IrrEvent.KeyInput.Shift );
    int mod         =   (m_IrrEvent.KeyInput.Shift ? KeyEvent::SHIFT : 0) | 
                        (m_IrrEvent.KeyInput.Control ? KeyEvent::CTRL : 0 ) |
                        (m_IrrEvent.KeyInput.PressedDown ? KeyEvent::BUTTON_DOWN : KeyEvent::BUTTON_RELEASED);

    KeyEvent    keyEvent( keyStroke, mod );
    if ( m_IrrEvent.KeyInput.PressedDown )
    {
        // make sure the key is in bounds
        if ( keyStroke >= 0 && keyStroke < MAX_KEYS ) {
            
            // check if this is a newly pressed key
            if ( key[ keyStroke ].TimeStamp == 0 )
            {
                key[ keyStroke ].isDown = true;
                key[ keyStroke ].Released = false;
                // set the time stamps
                key[ keyStroke ].TimeStamp  = g_kernel->GetTime();
                key[ keyStroke ].NextTime   = 0;        // next time to allow key to process
                key[ keyStroke ].TotalTime  = 0;        // time to let key be held down

                ProcessKeyEvent( &keyEvent, true );
#ifdef _DEBUG
                if ( g_kernel->GetCvars()->GetFloatValue( "d_keysym" ) )
                    std::cout << "Myriad Keysym: " << keyStroke << " vs Irr: " << m_IrrEvent.KeyInput.Key << "\n";
#endif
            } 
            //
            // Accumalate the time the key has been pressed
            else if ( key[ keyStroke ].TotalTime < DELAY_TIME )
            {
                key[ keyStroke ].TotalTime += dt;
            }
            //
            // The key is being held down, now let it rip
            //
            else if ( key[ keyStroke ].TotalTime > DELAY_TIME )
            {
                key[ keyStroke ].NextTime += dt;
                if ( key[ keyStroke ].NextTime > NEXT_TIME )
                {
                    key[ keyStroke ].isDown = true;
                    key[ keyStroke ].Released = false;
                    key[ keyStroke ].NextTime = 0;

                    ProcessKeyEvent( &keyEvent, true );
                }
            }
        }
    }
    else 
    {
        // clear the attributes
        if ( keyStroke >= 0 && keyStroke < MAX_KEYS /*&& key[ keyStroke ].NextTime > g_kernel->GetTime()*/ ) 
        {
            key[ keyStroke ].isDown    = false;
            key[ keyStroke ].Released  = true;
            key[ keyStroke ].TimeStamp = 0;
            key[ keyStroke ].NextTime  = 0;
            key[ keyStroke ].TotalTime = 0;

            ProcessKeyEvent( &keyEvent, false );
        }
    }
}

/** Process the events to the Listeners */
void IrrInput::ProcessKeyEvent( KeyEvent* e, bool keyDown )
{
    type_KeyListenerList::iterator it = m_KeyListeners.begin();
    for(; it != m_KeyListeners.end(); ++it )
    {
        if( (*it)->HasKeyFocus() )
        {
            if ( keyDown )
                (*it)->KeyPressed( e );
            else
                (*it)->KeyReleased( e );
        }
    }
}

/** Process a mouse event to the listeners */
void IrrInput::ProcessMouseEvent( MouseEvent* e )
{
    int type = e->GetModifiers();
    type_MouseListenerList::iterator it = m_MouseListeners.begin();
    for(; it != m_MouseListeners.end(); ++it )
    {
        if( (*it)->HasMouseFocus() )
        {
            if ( type & e->MOTION )
                (*it)->MouseMotion( e );
            else if ( type & e->BUTTON_RELEASED )
                (*it)->MouseButtonReleased( e );
            else if ( type & e->BUTTON_DOWN )
                (*it)->MouseButtonPressed( e );
        }
    }
}


/*----------------------------
    Handle Joystick Input
-----------------------------*/

/** Test to see if there is a joystick availible, return the count */
int IrrInput::CheckJoystick()
{
    return 1;
}

/** Enable the joystick module */
void IrrInput::EnableJoystick()
{

}
/** Check the x-axis */
int IrrInput::GetJoystickX()
{
return 1;
}
/** Check the y-axis */
int IrrInput::GetJoystickY()
{
return 1;
}
/** Test to see if a button is down */
int IrrInput::IsJoystickButtonDown(int button, bool stamp)
{
return 1;
}
/*----------------------------
    Handle the mouse 
-----------------------------*/

/** Get the mouse x-pos */
int IrrInput::GetMouseX()
{
    return m_IrrEvent.MouseInput.X;
}
/** Get the mouse y-pos */
int IrrInput::GetMouseY()
{
    return m_IrrEvent.MouseInput.Y;
}
/** Get the change in x */
int IrrInput::GetDeltaMX()
{
    return m_IrrEvent.MouseInput.X;
}
/** Get the change in y */
int IrrInput::GetDeltaMY()
{
    return m_IrrEvent.MouseInput.X;
}
/** Test to see if a mouse button is down */
bool IrrInput::IsButtonDown( int button, bool stamp )
{
    return true;
}
/** Test to see if a button is released */
bool IrrInput::ButtonReleased( int button )
{
return true;
}
/*---------------------------------
    Handle keyboard 
----------------------------------*/

/** Test to see if a key is down */
bool IrrInput::IsKeyDown( int key_, bool stamp)
{
    bool old = key[key_].isDown;
    if ( !stamp )
        key[key_].isDown = false;
    return old;
}
/** Test to see if a key is released */
bool IrrInput::KeyReleased( int key_ )
{
    return key[ key_ ].Released;
}












